package net.miranti.client.view;

import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HTMLTable.Cell;
import com.google.gwt.user.client.ui.HTMLTable.RowFormatter;

public abstract class DataGridWidget<T> extends Composite {
	protected static final String DEFAULT_SELECTED_STYLE = "datagrid-selected";
	protected VerticalPanel panel = new VerticalPanel();
	protected HTML gridTitle = new HTML(), emptyResultMessage = new HTML();
	protected Grid grid = new Grid();
	
	protected abstract String getGridTitleText();
	protected abstract String[] getHeaders();
	protected abstract Widget[] getData(T element);

	protected DataGridWidget() { }
	
	public void initDataGrid() {
		String title = getGridTitleText();
		if (title!=null)
			gridTitle.setHTML("<h2>" + title + "</h2>");
		gridTitle.setVisible(false);
		emptyResultMessage.setHTML(getEmptyResultMessageText());	
		emptyResultMessage.setVisible(false);

		grid.setStyleName("datagrid");
		
		panel.add(gridTitle);
		panel.add(emptyResultMessage);
		panel.add(grid);
		initWidget(panel);
		
		grid.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				Cell cell = grid.getCellForEvent(event);
				if (cell!=null)
					handleSelection(cell.getRowIndex(), cell.getCellIndex()); 
			}
		});
	}
	
	// default implementation: add highlight function for every table
	// as a facility to reading the table
	protected void handleSelection(int row, int col) {
		if (row==0)
			return;
		RowFormatter rf = grid.getRowFormatter();
		String currentStyle = rf.getStyleName(row);
		if (currentStyle.equals(DEFAULT_SELECTED_STYLE))
			rf.removeStyleName(row, DEFAULT_SELECTED_STYLE);
		else rf.setStyleName(row, DEFAULT_SELECTED_STYLE);
	}
	
	public void populate(T[] elements) {		
		gridTitle.setVisible(true);
		boolean hasElements = elements.length > 0;
		emptyResultMessage.setVisible(!hasElements);
		grid.setVisible(hasElements);
		clearDataFormatting();
		if (hasElements) {
			setupHeaders(elements);
			RowFormatter rf = grid.getRowFormatter();
			for(int i=1; i<=elements.length; i++) {
				T e = elements[i-1];
				Widget data[] = getData(e);
				for (int j=0; j<data.length; j++)
					grid.setWidget(i, j, data[j]);
				String id = getId(e);
				if (id!=null)
					rf.getElement(i).setId(id);
			}
			grid.setVisible(true);
		}
	}
	
	protected void setupHeaders(T[] elements) {
		String[] headers = getHeaders();
		grid.resize(elements.length + 1, headers.length);
		
		RowFormatter rf = grid.getRowFormatter();
		rf.getElement(0).setClassName("datagrid-header");
		for(int i=0; i<headers.length; i++)
			grid.setHTML(0, i, headers[i]);
	}
	
	// default implementation - does nothing
	protected String getId(T element) {
		return null;
	}
	
	protected String getEmptyResultMessageText() {
		return "";
	}

	public void highlightRows(final String style, final Object[] ids) {
		// command is deferred to guarantee the view was already attached by external handlers
		// otherwise, DOM searches would return null, and no effect would be produced
		DeferredCommand.addCommand(new Command() {
			public void execute() {
				clearDataFormatting();
				for (Object id : ids)
					DOM.getElementById(id.toString()).setClassName(style);
			}
		});
	}

	public void highlightRow(String rowId, String style) {
		highlightRow(rowId, style, true);
	}
	
	public void highlightRow(final String id, final String style, final Boolean addStyle) {
		// command is deferred to guarantee the view was already attached by external handlers
		// otherwise, DOM searches would return null, and no effect would be produced
		DeferredCommand.addCommand(new Command() {
			public void execute() {
				Element e = DOM.getElementById(id);
				if (addStyle)
					e.addClassName(style);
				else e.removeClassName(style);
			}
		});
	}
	
	public void clearDataFormatting() {
		RowFormatter rf = grid.getRowFormatter();
		int n = grid.getRowCount();
		for (int i=1; i<n; i++) {
			Element e = rf.getElement(i);
			String clazz = e.getClassName();
			if (!clazz.isEmpty())
				e.removeClassName(clazz);
		}
	}

	public void clear() {
		emptyResultMessage.setVisible(false);
		gridTitle.setVisible(false);
		grid.setVisible(false);
		grid.resize(0, 0);
	}
}